home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / DTS.Lib / DTS.Lib.headers / TreeObj.h < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-02  |  80.0 KB  |  1,895 lines  |  [TEXT/MPS ]

  1. #ifndef __TREEOBJ__
  2. #define __TREEOBJ__
  3.  
  4. #ifndef __TYPES__
  5. #include <Types.h>
  6. #endif
  7.  
  8. #ifndef __ERRORS__
  9. #include <Errors.h>
  10. #endif
  11.  
  12. #ifndef __EVENTS__
  13. #include <Events.h>
  14. #endif
  15.  
  16. #ifndef __MEMORY__
  17. #include <Memory.h>
  18. #endif
  19.  
  20. #define NO_EDIT 0                        /* System edit type. */
  21.  
  22.  
  23. /********/
  24.  
  25.  
  26. #define EMPTYOBJ    0
  27. #define ROOTOBJ     1
  28. #define UNDOOBJ     2
  29. #define UNDOTASKOBJ 3
  30. #define UNDOPARTOBJ 4
  31. #define WFMTOBJ     5
  32. #define CTLOBJ      6
  33.  
  34. #define kCStr       0
  35. #define kPStr       1
  36. #define kSDataBlock 2
  37. #define kLDataBlock 3
  38.  
  39. #ifdef powerc
  40. #pragma options align=mac68k
  41. #endif
  42. struct FileRec;
  43. typedef struct FileRec *FileRecPtr, **FileRecHndl;
  44. #ifdef powerc
  45. #pragma options align=reset
  46. #endif
  47.  
  48. #ifdef powerc
  49. #pragma options align=mac68k
  50. #endif
  51. struct TreeObj;
  52. typedef struct TreeObj *TreeObjPtr, **TreeObjHndl;
  53. typedef long    (*TreeObjProcPtr)(TreeObjHndl hndl, short message, long data);
  54. #ifdef powerc
  55. #pragma options align=reset
  56. #endif
  57.  
  58. #ifdef powerc
  59. #pragma options align=mac68k
  60. #endif
  61. typedef struct TreeObj {
  62.     short        type;
  63.     short        numChildren;
  64.     long        dataSize;
  65.     long        treeID;
  66.     TreeObjHndl    parent;
  67. } TreeObj;
  68. #ifdef powerc
  69. #pragma options align=reset
  70. #endif
  71.  
  72. #ifdef powerc
  73. #pragma options align=mac68k
  74. #endif
  75. long    TUndoObj(TreeObjHndl hndl, short message, long data);
  76. typedef struct {
  77.     TreeObjHndl    root;
  78.     FileRecHndl    frHndl;
  79.     Boolean        disabled;
  80.     short        lastEditType;
  81.     short        undoDepth;
  82.     short        maxNumUndos;
  83.     short        numSaveUndos;
  84. } UndoObj;
  85. #ifdef powerc
  86. #pragma options align=reset
  87. #endif
  88.  
  89. #ifdef powerc
  90. #pragma options align=mac68k
  91. #endif
  92. long    TUndoTaskObj(TreeObjHndl hndl, short message, long data);
  93. typedef struct {
  94.     short    editType;
  95.     Point    undoOrigin;
  96.     Point    redoOrigin;
  97. } UndoTaskObj;
  98. #ifdef powerc
  99. #pragma options align=reset
  100. #endif
  101.  
  102. #ifdef powerc
  103. #pragma options align=mac68k
  104. #endif
  105. long    TUndoPartObj(TreeObjHndl hndl, short message, long data);
  106. typedef struct {
  107.     short        actionType;
  108.     TreeObjHndl    shndl;
  109.     short        scnum;
  110.     TreeObjHndl    dhndl;
  111.     short        dcnum;
  112.     Boolean        deepCopy;
  113. } UndoPartObj;
  114. #ifdef powerc
  115. #pragma options align=reset
  116. #endif
  117.  
  118. #ifdef powerc
  119. #pragma options align=mac68k
  120. #endif
  121. struct VHFormatData {
  122.     Handle    text;
  123.     char    *header;
  124.     char    *data;
  125. };
  126. typedef struct VHFormatData VHFormatData;
  127. typedef VHFormatData *VHFormatDataPtr;
  128. #ifdef powerc
  129. #pragma options align=reset
  130. #endif
  131.  
  132. #define mDerefUndo(hndl)     ((UndoObj*)((*hndl) + 1))
  133. #define mDerefUndoTask(hndl) ((UndoTaskObj*)((*hndl) + 1))
  134. #define mDerefUndoPart(hndl) ((UndoPartObj*)((*hndl) + 1))
  135.  
  136.  
  137. /********/
  138.  
  139.  
  140. #define INITMESSAGE        0        /* Additional object initialization. */
  141. #define     CREATEINIT         0    /* Object being initially created. */
  142. #define     WINDOWINIT         1    /* Document/object being assigned to a window. */
  143. #define     NOWINDOWINIT       2    /* Document/object being freed from a window. */
  144.  
  145. #define FREEMESSAGE        1        /* Additional object disposal. */
  146.  
  147. #define COPYMESSAGE        2        /* Additional object cloning. */
  148.  
  149. #define UNDOMESSAGE        3        /* Additional object undo. */
  150. #define     UNDOFROMDOC        0    /* Object leaving document. */
  151. #define     UNDOTODOC          1    /* Object returning to document. */
  152.  
  153. #define CONVERTMESSAGE     4        /* Hndl2ID or ID2Hndl conversions. */
  154. #define     CONVERTTOID        0    /* Convert handle references to ID references. */
  155. #define     CONVERTTOHNDL    1        /* Convert ID references to handle references. */
  156.  
  157. #define FREADMESSAGE       5        /* Read object data from file. */
  158. #define FWRITEMESSAGE      6        /* Write object data to file. */
  159.  
  160. #define HREADMESSAGE       7        /* Read object data from handle. */
  161. #define HWRITEMESSAGE      8        /* Write object data to handle. */
  162.  
  163. #define HITTESTMESSAGE     9        /* Test if object was hit. */
  164. #define     HITTESTOBJ         0    /* Test body of object for hit. */
  165. #define     HITTESTGRABBER     1    /* Test sizing parts for hit. */
  166. #define     CANBETARGET        2    /* Return true if object can become target. */
  167. #define     CANTAKEKEYS        3    /* Return true if target can take keys. */
  168.  
  169. #define GETRGNMESSAGE      10        /* Return object defining rect. */
  170. #define GETOBJRECTMESSAGE  11        /* Return object defining rect. */
  171. #define SETOBJRECTMESSAGE  12        /* Set object bounding box. */
  172. #define SECTOBJRECTMESSAGE 13        /* Check if rect intersects object bounding box. */
  173.  
  174. #define GETBBOXMESSAGE     14        /* Return rect that contains entire graphic image. */
  175.  
  176. #define DRAWMESSAGE        15        /* Draw some form of the object. */
  177. #define     DRAWOBJ            0    /* Draw the object. */
  178. #define     ERASEOBJ           1    /* Draw the object. */
  179. #define     DRAWSELECT         2    /* Draw the selection portion of the object. */
  180. #define     DRAWGHOST          3    /* Draw an xor-ghost (for dragging) of the image. */
  181. #define     DRAWMASK           4    /* Draw a mask of the image (for offscreen layer masking.) */
  182.  
  183. #define PRINTMESSAGE       16
  184.  
  185. #define VHMESSAGE          17        /* Format View Hierarchy information for the object. */
  186.  
  187. #define COMPAREMESSAGE     18        /* Compare data of two objects of same type. */
  188.  
  189.  
  190.  
  191. #define DOUNDO 0
  192. #define DOREDO 1
  193.  
  194.  
  195.  
  196. TreeObjHndl    NewRootObj(short ctype, long size);
  197.  
  198. /*
  199. ¶ Create a root object, i.e., one that has no parent object.
  200.  
  201. INPUT:    ctype        Type of object to create, such as ROOTOBJ, WFMTOBJ, etc.
  202.         size        Initial size of root object.  Note that there is a min-size table,
  203.                     so this is used only if it is greater than the default size.  If
  204.                     the default size is what you want, just pass in 0 here.
  205.  
  206. Create a root object, i.e., one that has no parent object.
  207.  
  208. Defining a generic document architecture that is flexible enough to handle the many and varied
  209. document types, yet simple enough to justify using it, can be difficult.
  210.  
  211. The document structure type chosen here is a simple hierarchy.  For simple document types, the
  212. hierarchical feature can simply be ignored.  All of the member objects of the document can simply be
  213. children of the root object.  This gives the document a linear feel, (all objects are at the same
  214. level in the hierarchy), which is all that is needed for a lot of applications.
  215.  
  216. For more complex document structures that have many data types which inter-relate, the hierarchical
  217. model is quite useful.  The sample given here is a draw program.  The draw example demonstrates both
  218. the linear document type (no grouping used) and the hierarchical document type (grouping used).
  219. If no grouping is used, then all of the objects added to the document are children of the root
  220. object.  The objects are arranged linearly off the root document object.  If some of the objects
  221. are grouped, a group object is first added to the root object, and then the selected objects are
  222. moved so that they are children of the group object.  Now the document is no longer linear.  The
  223. root object has children, some of which may be group objects.  These group objects in turn have
  224. children.
  225.  
  226. This sort of hierarchical support makes grouping of objects in a draw-type program trivial.
  227. It naturally represents the organization of the objects within the document.
  228.  
  229. An additional benefit of managing the objects in a document in such a standardized way is that
  230. additional application features such as undo/redo can be automatically supported.  Whenever a change
  231. is to be made to the document, one of a few document-hierarchy-management calls is made first.
  232. The different operations that can be done to the hierarchical document are:
  233.  
  234. All that is needed to create a root object is to call NewRootObj.  A root object is simply a standard
  235. object with no parent.  (The “parent” field for a root object is therefore nil.)
  236.  
  237.  
  238. The default AppsToGo object is initially created with a root object.  The root object can have data
  239. stored directly into the data area within it, or instead, children can be added to the root object.
  240. The more flexible choice, of course, is to add children to the root object.
  241.  
  242.  
  243. To add children to the root object, you would simply call NewChild, giving the root object as the
  244. object to add a child to.  See NewChild for more information on this.
  245. */
  246.  
  247.  
  248.  
  249.  
  250. TreeObjHndl    NewChild(short editType, TreeObjHndl parentHndl, short childNum, short ctype, long size);
  251.  
  252. /*
  253. ¶ Creates a child of specified type and adds it to parent at specified location.
  254.  
  255. Creates a child of specified type and adds it to parent at specified location.
  256. Returns nil upon failure.
  257.  
  258. NewChild is one of six operations that can be done to a hierarchical document that has automatic
  259. undo/redo support.  The six operations are:
  260.  
  261.     NewChild
  262.     DisposeChild
  263.     CopyChild
  264.     MoveChild
  265.     ModifyChild
  266.     SwapChildren
  267.  
  268. These six operations cover the various operations that can be performed to one or more of the
  269. document objects.  Children can be added, removed, copied, moved, modified, or swapped.  If these
  270. calls are used to modify objects within the document hierarchy, then the editing operations done to
  271. the document can automatically be recorded for undo/redo purposes.
  272.  
  273. To better understand the hierarchical document architecture, we will talk about more than just
  274. NewChild here.  Since many of the function inter-relate, it is best to discuss them in a group
  275. at some point.
  276.  
  277. Looking at a sample NewChild call and its prototype, we have:
  278.  
  279. NewChild, if successful, returns a handle for the child object added to the document.
  280.  
  281. The parameters are:
  282.     editType:    Application edit type for which this document modification is being done.
  283.     parentHndl:    The child created will be added as a child to this object.
  284.     childNum:    The child will be added to the parent as this child number.
  285.     ctype:        The child will be of this type.
  286.     size:        The child will be created this initial size (if size is greater than minimum).
  287.  
  288. NewChild must have a parent declared.  This begs the question of where the initial parent comes from.
  289. At some point, NewRootObj must be called to create the initial root parent.  As would be expected,
  290. the root object has no parent.  Once there is an initial root object, children can then be added to
  291. the document via NewChild.
  292.  
  293. The editType of the NewChild call is used for automatically tracking undo/redo.  Many individual
  294. operations may be done to the document that are all for the same editing operation.  Let's say that
  295. you have a word-processor application that has paragraph objects.  If your document has 27
  296. paragraphs, then you have 27 children off the root object.  The order of the children is the order
  297. of the paragraphs in your document.
  298.  
  299. Let's now say that the user has selected some text.  The text selected starts in the middle of
  300. paragraph 3, goes through paragraphs 4-5, and through half of the text in paragraph 6.  The user
  301. then deletes this text.  We may have declared this type of edit to be of type DELETE_EDIT.
  302. The edits to the document that we will perform are:
  303.  
  304. 1) Modify paragraph 3 by removing the selected text and keeping the unselected text.
  305. 2) Delete paragraphs 4-5.
  306. 3) Modify paragraph 6 by removing the selected text and keeping the unselected text.
  307.  
  308. For step 1, we would first use ModifyChild.  The prototype for ModifyChild is:
  309.  
  310.     OSErr    ModifyChild(short editType, TreeObjHndl phndl, short childNum, Boolean deepCopy);
  311.  
  312. ModifyChild makes a copy of the object and places the copy in the undo hierarchy.  We are now free
  313. to modify the object any way we see fit.  If the user chooses to undo the change, the undo code
  314. simply swaps the data of the modified object with the copy that was saved in the undo hierarchy.
  315. If the user later chooses to redo the edit, the undo code simply swaps the data again to perform
  316. the redo.
  317.  
  318. NOTE:  ModifyChild may fail, since a copy of the object is made.  There may not be enough memory
  319.        for this copy to be created.  If this is so, then memFullErr will be returned.
  320.  
  321. The parameters are:
  322.     editType:    Application edit type for which this document modification is being done.
  323.     parentHndl:    Parent of the child being modified.
  324.     childNum:    Child number (of the parent) being modified.
  325.     deepCopy:    If true, the children of this child are also copied into the undo hierarchy.
  326.  
  327. The editType parameter is used to determine if this document modification is of the same
  328. edit type as previous document modifications.  If it is, then the operation is grouped
  329. with the other operations.  This is all done for the purposes of undo/redo.  Multiple objects
  330. may be modified in a single edit, and these modifications all have to be undone/redone with
  331. a single undo/redo.
  332.  
  333. If the editType is different than the last document modification, then a new undo group is
  334. started, and this operation is recorded in this new group.
  335.  
  336. For the above example, we would make the following call to ModifyChild prior to removing
  337. the selected text from paragraph 3:
  338.  
  339.     err = ModifyChild(DELETE_EDIT, root, 3, false);
  340.  
  341. Of course, it would be a really odd application in which the '3' was hard-coded as in
  342. the above line.
  343.  
  344. Now paragraphs 4 and 5 need to be deleted.  This is done by using DisposeChild.
  345. The prototype for DisposeChild is:
  346.     void    DisposeChild(short editType, TreeObjHndl parentHndl, short childNum);
  347.  
  348. Note that DisposeChild will succeed, as it doesn't have to create a new handle for the
  349. copy as ModifyChild does.  HOWEVER:  DisposeChild doesn't necessarily free up ram, as
  350. you would expect.  All that occurs is that the child is moved into the undo hierarchy so
  351. that undo can move the child back into the document.
  352.  
  353. To delete children 4 and 5, we would do the following:
  354.  
  355.     DisposeChild(DELETE_EDIT, root, 4);
  356.     DisposeChild(DELETE_EDIT, root, 4);
  357.  
  358. Note that for both calls we dispose of child number 4.  This is because once the first child
  359. is disposed of, what used to be child 5 is now child 4.
  360.  
  361. The last operation we would do is to declare our intent to change to the end child in the
  362. selection range.  This would be done as follows:
  363.     err = ModifyChild(DELETE_EDIT, root, 4, false);
  364.  
  365. The delete (assuming no errors) has now been completed.  Actually, the delete has occurred
  366. in either case.  If there were errors, all that occurred is that the undo information wasn't
  367. recorded.
  368.  
  369. Now let's say that the user selects a new range of text and deletes it, as well.  We would
  370. do the same thing as above to delete the text.  Here's the problem:  The editType of this
  371. second text delete is the same as the first.  Both document edits were of type DELETE_EDIT.
  372. This means that both operations were recorded under the same undo task.  When the user
  373. chooses undo, both deletes will be undone.  This isn't what we want.
  374.  
  375. To fix this problem, we have to make one additional call.  We need to call NewUndo.
  376. NewUndo closes out the old editing task.  When you then make some document modification,
  377. it will be recorded in a separate undo task.
  378.  
  379. NewUndo only takes a single parameter.  Just pass it any handle in the document.  Any
  380. object in the document can serve as a reference to the document.
  381.  
  382.  
  383. An object handle has 3 parts:
  384. 1) The header information.
  385. 2) The application-defined object data.
  386. 3) The child handle table.
  387.  
  388.  
  389. The object header is of the following form:
  390.  
  391. typedef struct TreeObj {
  392.     short        type;
  393.     short        numChildren;
  394.     long        dataSize;
  395.     long        treeID;
  396.     TreeObjHndl    parent;
  397. } TreeObj;
  398.  
  399. These fields are automatically filled out whenever an object is created via NewRootObj
  400. or NewChild.
  401.  
  402. The application-defined data area is automatically initialized with 0's.  When NewRootObj
  403. or NewChild is called, a size parameter is passed in.  There is also a table of minimum sizes
  404. for each object type that needs to be defined.  If the size passed in is smaller than the
  405. minimum size, then the minimum size is used as the data size instead.
  406.  
  407. The minimum size table is a global array of longs called gMinTreeObjSize.  Its definition is
  408. found in File.c.  For each type of object defined, there needs to be a minimum size defined.
  409.  
  410. There are some predefined object types.  These are defined by the following:
  411.  
  412. #define ROOTOBJ     1
  413. #define UNDOOBJ     2
  414. #define UNDOTASKOBJ 3
  415. #define UNDOPARTOBJ 4
  416. #define WFMTOBJ     5
  417. #define CTLOBJ      6
  418.  
  419. These are the mandatory object types for supporting this implementation of the document
  420. structure and undo/redo features.
  421.  
  422. Application-defined object types can start with type number 16.  Type number 0 is reserved
  423. and is currently undefined.  Types 7-15 are reserved for future objects that may be added
  424. to the DTS.Lib application framework.
  425.  
  426. The numChildren field is initially 0 after calling either NewRootObj or NewChild.
  427.  
  428. The dataSize is the effective data size after object creation, which is either the size passed
  429. in, or the minimum object size, whichever is greater.
  430.  
  431. The treeID field is initially set to 0 by both NewRootObj and NewChild.  This field is
  432. used to uniquely identify members of the hierarchy by ID.  (More on this later.)
  433.  
  434. The parent field is a reference to the handle which is the parent.  As you would expect,
  435. objects created by NewRootObj have no parent, and therefore this field is nil.
  436.  
  437.  
  438. The application-defined data area starts immediately after the header information.  As the
  439. structure of this portion is application-defined and variant, there is no structure pre-defined
  440. for it.  Each object type will have a unique structure definition for this portion of the object.
  441.  
  442.  
  443. The final part of an object is the child handle table.  The child handle table starts
  444. immediately after the data portion of the object.  The size of the header structure plus
  445. the value in the dataSize field serves as an offset from the beginning of the object to
  446. the child handle table.  Given the relationship between dataSize and the child handle
  447. table, it is important that the object isn't resized directly.  There are calls for managing
  448. the data area and its size that take the child handle table into account.  These are:
  449.  
  450.     OSErr    AdjustDataSize(TreeObjHndl hndl, long delta);
  451.     OSErr    SetDataSize(TreeObjHndl hndl, long newSize);
  452.     OSErr    SlideData(TreeObjHndl hndl, long offset, long delta);
  453.     void    *GetDataPtr(TreeObjHndl hndl);
  454.  
  455. AdjustDataSize alters the size of the data area, based on the delta passed in.  If you wish
  456. to decrease the data area by one byte, for example, pass in a -1.  AdjustDataSize does not
  457. reposition any of the data within the data area.  It does move the child handle table so that
  458. it continues to be positioned immediately after the data area.
  459.  
  460. SetDataSize does as expected.  It specifically sets the data size to the designated
  461. size.  It also does not shift the data, but does do the appropriate child handle table
  462. maintenance, just as AdjustDataSize does.
  463.  
  464. SlideData operates the same as AdjustDataSize, plus it also slides some or all of the
  465. data in the data area.  Use SlideData to insert or remove data at some location within
  466. the data area.
  467.  
  468. GetDataPtr simply returns a pointer to the beginning of the data area.  IT DOES NOT LOCK
  469. THE HANDLE!!  If the handle needs locking, then you must do this yourself.  Since the object
  470. is simply a handle, this poses no difficulties.  (Unlock when you are done, of course.)
  471.  
  472.  
  473. The child handle table is automatically handled.  There shouldn't be a reason that you need
  474. to manage or reference this yourself.  If however you end up needing to do this, there
  475. are calls to manage the child handle table.
  476.  
  477.  
  478. Objects referencing other objects:
  479.  
  480. Let's revisit the word-processor example.  Once again, we have this paragraph-based word
  481. processor.  The user selects some text.  The cursor location (insertion point) must be kept.
  482. A reference to the starting object and an offset into the data of that object would serve as
  483. the cursor location reference.
  484.  
  485. This reference to the paragraph object containing the insertion point could be kept as either
  486. a handle reference or a child number reference.  Either would serve the purpose.  Both would
  487. uniquely describe an object in the document.  The handle reference would directly reference
  488. the object.  The child number reference would indirectly reference the object.  If we used
  489. child numbers as our reference and we desired to get the handle of the object, we would do
  490. something like the following:
  491.  
  492.     chndl = GetChildHndl(root, cnum);
  493.  
  494. In our document example, all of the paragraph objects are children of the root object.
  495. In this case it would be a simple matter of getting the child handle.  The child number
  496. reference would probably be stored in the root object, as it is a global piece of information
  497. for this document.  We would probably want it saved with the document, which would occur
  498. automatically if it were stored in the root object.
  499.  
  500. So, since they both seem to work, which is better?  Is keeping the handle better, or is keeping
  501. the child number better?  When saving a document, child numbers will be meaningful when the
  502. document is opened at a future date.  References to handles aren't meaningful when saved to disk
  503. and then reloaded.  For this reason it seems that child number should win out.
  504.  
  505. But what if you wish to keep a reference to some arbitrary point in the document?  What if you
  506. don't know what the parent of that object is?  What if the object is at some arbitrary depth
  507. in the hierarchy?  In this case, it seems pretty clear that we wish to use handle references
  508. instead of child number references, at least when the document is in memory.
  509.  
  510. So what about handle references when saved to disk?  We need these references to persist.  To
  511. do this, we must convert them to something that saves meaningfully and can be converted back
  512. to handle references when the document is reopened.
  513.  
  514. This is what the treeID field in the object header is for.  When DoNumberTree is called,
  515. all of the objects in the hierarchy are uniquely numbered.  DoNumberTree is automatically
  516. called by the shell just prior to the objects in a hierarchy are written to disk.  In
  517. addition, prior to writing an object out to disk, the object is called with a message
  518. requesting it to convert any handle references it contains to treeID references.  For each
  519. handle reference that needs to be converted, you need to call Hndl2ID to do the conversion.
  520. Hndl2ID depends on DoNumberTree already being called so that all of the treeID fields
  521. for all of the objects in the hierarchy are current.
  522.  
  523. After calling the object with the handle-to-id conversion message, the object is written
  524. to disk.  Once it is written, a message is sent to the object requesting it to covert the
  525. id back into a handle reference.  For each reference converted, you need to call ID2Hndl
  526. to deconvert the reference back into a handle.
  527.  
  528. When a document is opened, all of the objects are first read into memory.  Once the entire
  529. document is in memory, DoNumberTree is called, and then each object in the document is
  530. sent a message requesting it to convert the converted handle references back into real
  531. handle references.
  532.  
  533. The reason that the entire document must first be read is that the reference to another object
  534. may be to an object that is later in the document.  Only after the entire document is read in
  535. is it possible to resolve all references to anywhere in the document.
  536.  
  537. This messaging mechanism allows you to use handle references within your application without
  538. worrying about them persisting through a save/open cycle.  So, given the above messaging
  539. mechanism, using either child number references or handle references is equally valid.
  540. Whatever seems easiest for a particular application is the one to use.
  541.  
  542.  
  543. There is a standard set of messages passed to objects.  The above message is just one of these.
  544. See DoTreeObjMethod for a complete list of standard messages and descriptions.
  545.  
  546. */
  547.  
  548.  
  549.  
  550. void        DisposeChild(short editType, TreeObjHndl parentHndl, short childNum);
  551.     /*
  552.     **    ¶ Disposes of the specified child and all offspring of that child.
  553.     **
  554.     **    INPUT:    editType        Edit type for undo posting (NO_EDIT for no posting).
  555.     **            parentHndl        Parent handle that owns child to dispose.
  556.     **            childNum        Child number to dispose.
  557.     **
  558.     **
  559.     **    Disposes of the specified child and all offspring of that child.  Note that, unless editType
  560.     **    is NO_EDIT, all information necessary to undo this operation is automatically posted. */
  561.  
  562.  
  563.  
  564. TreeObjHndl    CopyChild(short editType, TreeObjHndl shndl, short scnum,
  565.                       TreeObjHndl dhndl, short dcnum, Boolean deepCopy);
  566.     /*
  567.     **    ¶ Copies specified child (and all offspring of that child, if desired).
  568.     **
  569.     **    INPUT:    editType        Edit type for undo posting (NO_EDIT for no posting).
  570.     **            shndl            Source parent handle for copy.
  571.     **            scnum            Source child number (of parent) to copy.
  572.     **            dhndl            Destination parent for copy.
  573.     **            dcnum            Destination child number (of parent) for copy.
  574.     **            deepCopy        True if children of copied child are to be copied too.
  575.     **    RESULT    TreeObjHndl        Handle new new child created due to copy operation.
  576.     **
  577.     **    Copies the specified child (and all offspring of that child if deepCopy is true).
  578.     **    Note that, unless editType is NO_EDIT, all information necessary to undo this operation
  579.     **    is automatically posted. */
  580.  
  581.  
  582.  
  583. OSErr        MoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum);
  584.     /*
  585.     **    ¶ Moves a child from one location on the tree to another.
  586.     **
  587.     **    INPUT:    editType    Type of edit for posting undo tasks.  For no undo posting, then
  588.     **                        pass in NO_EDIT as a value.
  589.     **            shndl        Old parent handle (parent prior to move).
  590.     **            scnum        Old child number for the object to move.
  591.     **            dhndl        New parent handle (parent after the move).
  592.     **            dcnum        New child number after the ovject is moved.
  593.     **    RESULT:    OSErr        Most likely error is memFulErr.
  594.     **
  595.     **    Moves a child from one location on the tree to another.  This source and destination
  596.     **    don't have to be the same tree. */
  597.  
  598.  
  599.  
  600. OSErr        ModifyChild(short editType, TreeObjHndl phndl, short cnum, Boolean deepCopy);
  601.     /*
  602.     **    ¶ Saves a copy of the child in the undo hierarchy for undo/redo purposes.
  603.     **
  604.     **    INPUT:    editType    Type of edit for posting undo tasks.  For no undo posting, then
  605.     **                        pass in NO_EDIT as a value.
  606.     **            phndl        Parent handle of object to be modified.
  607.     **            cnum        Child number of the object to modify.
  608.     **            deepCopy    True if children of object are to also be copied into the undo stack.
  609.     **    RESULT:    OSErr        Most likely error is memFulErr.
  610.     **
  611.     **    Saves a copy of the child in the undo hierarchy for undo/redo purposes. */
  612.  
  613.  
  614.  
  615. OSErr        SwapChildren(short editType, TreeObjHndl hndla, short cnuma,
  616.                          TreeObjHndl hndlb, short cnumb);
  617.     /*
  618.     **    ¶ Swaps the child handles.
  619.     **
  620.     **    INPUT:    editType    Type of edit for posting undo tasks.  For no undo posting, then
  621.     **                        pass in NO_EDIT as a value.
  622.     **            hndla        Parent of one of the two children to swap.
  623.     **            cnuma        Child number of one of the two children to swap.
  624.     **            hndlb        Parent of other child to swap.
  625.     **            cnumb        Child number of other child to swap.
  626.     **    RESULT:    OSErr        Most likely error is memFulErr.
  627.     **
  628.     **    Swaps the child handles. */
  629.  
  630.  
  631.  
  632. OSErr        SwapTreeObjData(TreeObjHndl hndla, TreeObjHndl hndlb);
  633.     /*
  634.     **    ¶ Swaps the child data without swapping the handles.
  635.     **
  636.     **    INPUT:    editType    Type of edit for posting undo tasks.  For no undo posting, then
  637.     **                        pass in NO_EDIT as a value.
  638.     **            hndla        Parent of one of the two children whose data is to be swapped.
  639.     **            cnuma        Child number of one of the two children whose data is to swapped.
  640.     **            hndlb        Parent of other child involved in swap.
  641.     **            cnumb        Child number of other child involved in swap.
  642.     **    RESULT:    OSErr        Most likely error is memFulErr.
  643.     **
  644.     **    Swaps the child data without swapping the handles. */
  645.  
  646.  
  647.  
  648. void        DisposeObjAndOffspring(TreeObjHndl objHndl);
  649.     /*
  650.     **    ¶ This disposes of the object and any offspring of that object.
  651.     **
  652.     **    INPUT:    objHndl        Object to dispose (and all of its children).
  653.     **
  654.     **    This disposes of the object and any offspring of that object.  It does not remove
  655.     **    the object from the parent's child handle table.  The common usage is to remove a
  656.     **    hierarchy starting from the root object. */
  657.  
  658.  
  659.  
  660. OSErr        CopyChildren(TreeObjHndl shndl, TreeObjHndl dhndl);
  661.     /*
  662.     **    ¶ Copy the children (and their children) of one object to another.
  663.     **
  664.     **    INPUT:    shndl        Source object (whose children are copied).
  665.     **            dhndl        Destination object (where copies will be placed as children).
  666.     **    RESULT:    OSErr        Most likely error is memFullErr.
  667.     **
  668.     **    Copies the children (and children below that and so on) of one object to another object.
  669.     **    This is used internally by CopyChild for deep copies. */
  670.  
  671.  
  672.  
  673. OSErr        InsertChildHndl(TreeObjHndl parentHndl, TreeObjHndl childHndl, short childNum);
  674.     /*
  675.     **    ¶ Adds an existing child to a parent's child handle table.
  676.     **
  677.     **    INPUT:    parentHndl        Parent whose child handle table will have an entry inserted.
  678.     **            childHndl        Child handle to insert into parent handle
  679.     **            childNum        Index into child handle table for insert.
  680.     **    RESULT:    OSErr            memFullErr
  681.     **
  682.     **    Adds an existing child to a parent's child handle table. */
  683.  
  684.  
  685.  
  686. void        DeleteChildHndl(TreeObjHndl parentHndl, short childNum);
  687.     /*
  688.     **    ¶ Removes a child from the parent's child handle table.
  689.     **
  690.     **    INPUT:    parentHndl
  691.     **            childNum
  692.     **
  693.     **    Removes a child from the parent's child handle table.  Note that it does not delete
  694.     **    the child.  (Possibly a better name would have been DetachChild.) */
  695.  
  696.  
  697.  
  698. TreeObjHndl    GetRootHndl(TreeObjHndl hndl);
  699.     /*
  700.     **    ¶ Given an object handle, return the root handle.
  701.     **
  702.     **    INPUT:    hndl            Any handle in the document hierarchy.
  703.     **    RESULT    TreeObjHndl        The root object ofthe hierarchy.
  704.     **
  705.     **    Given an object handle, return the root handle.  This function simply iterates through
  706.     **    to the object's parent until the parent reference is nil.  It returns that object. */
  707.  
  708.  
  709.  
  710. TreeObjHndl    GetChildHndl(TreeObjHndl parentHndl, short cnum);
  711.     /*
  712.     **    ¶ Given a parent handle and a child number, this returns the child handle.
  713.     **
  714.     **    INPUT:    parentHndl        Object whose child table is to indexed.
  715.     **            cnum            Index into the parent's child table.
  716.     **                                To get the first child, pass in 0.
  717.     **                                To get the last child, pass in -1, or any
  718.     **                                    number >= to the number of children.
  719.     **                                The number of children is (*parentHndl)->numChildren
  720.     **    RESULT:    TreeObjHndl        The child indexed out of the parent's child table.
  721.     **
  722.     **    Given a parent handle and a child number, this returns the child handle. */
  723.  
  724.  
  725.  
  726. TreeObjHndl    *GetChildHndlPtr(TreeObjHndl parentHndl, short *childNum, short endCase);
  727.     /*
  728.     **    ¶ Return a pointer into the child handle table.
  729.     **
  730.     **    INPUT:    parentHndl        Object whose child table is to indexed.
  731.     **            *childNum        Index into the parent's child table.
  732.     **            endCase            How to handle the index-beyond-end case.
  733.     **                            If 0, then pointing just after the child handle table
  734.     **                                is invalid, and the pointer will be snapped to point
  735.     **                                to the last entry in the table.  The purpose for this
  736.     **                                case is to reference or replace an existing entry.
  737.     **                            If 1, then pointing just after the child handle table
  738.     **                                is valid.  The purpose for this case is to get a pointer
  739.     **                                for an insertion operation.
  740.     **    RESULT:    TreeObjHndl        The child indexed out of the parent's child table.
  741.     **
  742.     **    Return a pointer into the child handle table.  This also validates (and corrects) cnum so
  743.     **    that it is in range, if possible.  Depending on the usage, pointing to just after the child
  744.     **    handle table is either valid or invalid.  If a handle is being added to the table, then
  745.     **    pointing just after the table is valid.  If a handle is being removed or referenced, then
  746.     **    pointing just after the table is invalid.  The parameter endCase determines which case we are
  747.     **    dealing with.  If endCase is 0, then pointing just after the child handle table is invalid,
  748.     **    and if the cnum value passed in causes this, then nil is returned for the pointer.  If
  749.     **    endCase is 1, then pointing just after the child handle table is okay, and therefore nil will
  750.     **    never be returned as the pointer.  Any cnum value out of range will be corrected
  751.     **    (if possible) to be within range. */
  752.  
  753.  
  754.  
  755. short        GetChildNum(TreeObjHndl hndl);
  756.     /*
  757.     **    ¶ Given a child handle, return posstion of it in the parent's child handle table.
  758.     **
  759.     **    INPUT:    hndl    Handle to get child number for.
  760.     **
  761.     **    Given a child handle, this returns the child number of that handle in the parent's child
  762.     **    handle table. */
  763.  
  764.  
  765.  
  766. OSErr        AdjustDataSize(TreeObjHndl hndl, long delta);
  767.     /*
  768.     **    ¶ Adjust the data size, greater or smaller, depending on sign of 'delta'.
  769.     **
  770.     **    INPUT:    hndl        Target hierarchical document object.
  771.     **            delta        Amount to increase or decrease data area size of object.
  772.     **
  773.     **    Adjusts the data size, either greater or smaller, depending on the sign of 'delta'.
  774.     **    Since this modifies the child, if you are posting undo information, you need to call
  775.     **    ModifyChild with an editType other than NO_EDIT prior to calling this.  ModifyChild
  776.     **    will post the appropriate undo information prior to the modification of the child. */
  777.  
  778.  
  779.  
  780. OSErr        SetDataSize(TreeObjHndl hndl, long newSize);
  781.     /*
  782.     **    ¶ Sets the data size to newSize.
  783.     **
  784.     **    INPUT:    hndl        Handle to alter size of data area.    
  785.     **            newSize        New size of data area.  If shrunk, data will be lost.
  786.     **
  787.     **    Sets the data size to newSize. */
  788.  
  789.  
  790.  
  791. OSErr        SlideData(TreeObjHndl hndl, long offset, long delta);
  792.     /*
  793.     **    ¶ Slide some of the data in data area of object while resizing data area.
  794.     **
  795.     **    INPUT:    hndl        Handle of object whose data area is to changed.
  796.     **            offset        First byte in data to move.  All bytes from this byte to the end
  797.     **                        will be moved.
  798.     **            delta        Amount to move data.
  799.     **                        If delta > 0 then the handle is grown, and then the data is moved.
  800.     **                        If delta < 0 then the data is moved, and then the handle is shrunk.
  801.     **    RESULT:    OSErr        Move likely error is memFullErr.
  802.     **
  803.     **    Slides some of the data in the data portion of the object starting at 'offset' by a 'delta'
  804.     **    amount, either forward or backward, depending on the sign of 'delta'. */
  805.  
  806.  
  807.  
  808. void        *GetDataPtr(TreeObjHndl hndl);
  809.     /*
  810.     **    ¶ Returns a pointer to the beginning of the object's data area.
  811.     **
  812.     **    INPUT:    hndl        Object to generate a pointer to data area for.
  813.     **    RESULT:    void *        Pointer to data area.  Cast it to whatever is appropriate.
  814.     **
  815.     **    Returns a pointer to the beginning of the object's data area.  THIS DOES NOT LOCK THE HANDLE!
  816.     **    The pointer may become invalid if memory moves. */
  817.  
  818.  
  819.  
  820. OSErr        ReadTree(TreeObjHndl hndl, short fileRefNum);
  821.     /*
  822.     **    ¶ Read the designated AppsToGo file into ram.
  823.     **
  824.     **    INPUT:    hndl            Handle which will serve as the root for the hierarchy that is to be
  825.     **                            read in.  This doesn't have to be a true root.  It can be an existing
  826.     **                            node in a hierarchy.  A branch will be created in this case.
  827.     **            fileRefNum        The reference number for a previously opened file.  The file position
  828.     **                            should be set to point to a previously-saved AppsToGo hierarchy.
  829.     **    RESULT:    OSErr
  830.     **
  831.     **    Given an open file that has been previously written via WriteTree, this function is called
  832.     **    to read the file data into ram.  The root object for the file is already created by
  833.     **    InitDocument.  The file must be open, and the file position must be set to the byte location
  834.     **    where the root object of the file starts.  Once this is so, just call this function with a
  835.     **    reference to the root object and the open file refrence number. */
  836.  
  837.  
  838.  
  839. OSErr        ReadTreeObjData(TreeObjHndl hndl, short fileRefNum);
  840.     /*
  841.     **    ¶ Read in dataSize number of bytes into the object.
  842.     **
  843.     **    INPUT:    hndl        Object to read disk data into the data area for.
  844.     **            fileRefNum    File to read from.
  845.     **    RESULT:    OSErr
  846.     **
  847.     **    Read in dataSize number of bytes into the object. */
  848.  
  849.  
  850.  
  851. OSErr        WriteTree(TreeObjHndl hndl, short fileRefNum);
  852.     /*
  853.     **    ¶ Stream a hierarchy to disk.
  854.     **
  855.     **    INPUT:    hndl            Top node of the tree or branch to be written to disk.
  856.     **            fileRefNum        File to write to.
  857.     **    RESULT:    OSErr
  858.     **
  859.     **    Given an open file that is ready to be written to, this function is called to write the file
  860.     **    tree to the designated file. */
  861.  
  862.  
  863.  
  864. OSErr        WriteTreeObjData(TreeObjHndl hndl, short fileRefNum);
  865.     /*
  866.     **    ¶ Write out dataSize (field in hndl) number of bytes from the object.
  867.     **
  868.     **    INPUT:    hndl            Object whose data is to be written to disk.
  869.     **            fileRefNum        File to write data to.
  870.     **
  871.     **    Write out dataSize number of bytes from the object. */
  872.  
  873.  
  874.  
  875. void        DoBTreeMethod(TreeObjHndl hndl, short message, long data);
  876.     /*
  877.     **    ¶ Call each object of the tree (or branch) in back-to-front order.
  878.     **
  879.     **    INPUT:    hndl        Start of tree or branch for tree-walk.
  880.     **            message        Message to send to object.
  881.     **            data        Application-defined data, i.e., refcon.
  882.     **
  883.     **    Call the object for each member of the tree (or branch) starting from the back of the
  884.     **    tree working forward.
  885.     **
  886.     **    __________
  887.     **
  888.     **    Also see:    DoErrBTreeMethod, DoFTreeMethod, DoErrFTreeMethod. */
  889.  
  890.  
  891.  
  892. OSErr        DoErrBTreeMethod(TreeObjHndl hndl, short message, long data);
  893.     /*
  894.     **    ¶ Call each object of the tree (or branch) in back-to-front order and abort on error.
  895.     **
  896.     **    INPUT:    hndl        Start of tree or branch for tree-walk.
  897.     **            message        Message to send to object.
  898.     **            data        Application-defined data, i.e., refcon.
  899.     **    RESULT:    OSErr        Error, if any, that caused abort.
  900.     **
  901.     **    Call the object for each member of the tree (or branch) starting from the back of the
  902.     **    tree working forward.  If an object returns an error, abort the rest of the walk.
  903.     **
  904.     **    __________
  905.     **
  906.     **    Also see:    DoBTreeMethod, DoFTreeMethod, DoErrFTreeMethod. */
  907.  
  908.  
  909.  
  910. void        DoFTreeMethod(TreeObjHndl hndl, short message, long data);
  911.     /*
  912.     **    ¶ Call each object of the tree (or branch) in front-to-back order.
  913.     **
  914.     **    INPUT:    hndl        Start of tree or branch for tree-walk.
  915.     **            message        Message to send to object.
  916.     **            data        Application-defined data, i.e., refcon.
  917.     **
  918.     **    Call the object for each member of the tree (or branch) starting from the front of the
  919.     **    tree working backward.
  920.     **
  921.     **    __________
  922.     **
  923.     **    Also see:    DoBTreeMethod, DoErrBTreeMethod, DoErrFTreeMethod. */
  924.  
  925.  
  926.  
  927. OSErr        DoErrFTreeMethod(TreeObjHndl hndl, short message, long data);
  928.     /*
  929.     **    ¶ Call each object of the tree (or branch) in front-to-back order and abort on error.
  930.     **
  931.     **    INPUT:    hndl        Start of tree or branch for tree-walk.
  932.     **            message        Message to send to object.
  933.     **            data        Application-defined data, i.e., refcon.
  934.     **    RESULT:    OSErr        Error, if any, that caused abort.
  935.     **
  936.     **    Call the object for each member of the tree (or branch) starting from the front of the
  937.     **    tree working backward.  If an object returns an error, abort the rest of the walk.
  938.     **
  939.     **    __________
  940.     **
  941.     **    Also see:    DoBTreeMethod, DoErrBTreeMethod, DoFTreeMethod. */
  942.  
  943.  
  944.  
  945. long        DoTreeObjMethod(TreeObjHndl hndl, short message, long data);
  946. /*
  947. ¶ If the object has a method procedure, call it.  If no method procedure, do nothing.
  948.  
  949. Below is the list of standard messages, followed by a description of each and its use:
  950.  
  951. #define INITMESSAGE      0        Additional object initialization.
  952. #define     CREATEINIT       0        Object being initially created.
  953. #define     WINDOWINIT       1        Document/object being assigned to a window.
  954. #define     NOWINDOWINIT     2        Document/object being freed from a window.
  955.  
  956. #define FREEMESSAGE      1        Additional object disposal.
  957.  
  958. #define COPYMESSAGE      2        Additional object cloning.
  959.  
  960. #define UNDOMESSAGE      3        Additional object undo.
  961. #define     UNDOFROMDOC      0        Object leaving document.
  962. #define     UNDOTODOC        1        Object returning to document.
  963.  
  964. #define CONVERTMESSAGE   4        Hndl2ID or ID2Hndl conversions.
  965. #define     CONVERTTOID      0        Convert handle references to ID references.
  966. #define     CONVERTTOHNDL    1        Convert ID references to handle references.
  967.  
  968. #define FREADMESSAGE     5        Read object data from file.
  969. #define FWRITEMESSAGE    6        Write object data to file.
  970.  
  971. #define HREADMESSAGE     7        Read object data from handle.
  972. #define HWRITEMESSAGE    8        Write object data to handle.
  973.  
  974. #define HITTESTMESSAGE   9        Test if object was hit.
  975. #define     HITTESTOBJ       0        Test body of object for hit.
  976. #define     HITTESTGRABBER   1        Test sizing parts for hit.
  977. #define     CANBETARGET      2        Return true if object can become target.
  978. #define     CANTAKEKEYS      3        Return true if target can take keys.
  979.  
  980. #define GETRGNMESSAGE    10        Return object bounding box.
  981. #define GETBBOXMESSAGE   11        Return object bounding box.
  982. #define SETBBOXMESSAGE   12        Set object bounding box.
  983. #define SECTBBOXMESSAGE  13        Check if rect intersects object bounding box.
  984.  
  985. #define TARGETMESSAGE    14        Set target status for object.
  986. #define     TARGETOFF        0        Make the object no longer the target.
  987. #define     TARGETON         1        Make the object the target.
  988.  
  989. #define DRAWMESSAGE      15        Draw some form of the object.
  990. #define     DRAWOBJ          0        Draw the object.
  991. #define     ERASEOBJ         1        Draw the object.
  992. #define     DRAWSELECT       2        Draw the selection portion of the object.
  993. #define     DRAWGHOST        3        Draw an xor-ghost (for dragging) of the image.
  994. #define     DRAWMASK         4        Draw a mask of the image (for offscreen layer masking.)
  995.  
  996. #define PRINTMESSAGE     16        Print the object.
  997.  
  998. #define VHMESSAGE        17        Format View Hierarchy information for the object.
  999.  
  1000.  
  1001. INITMESSAGE:  This is called with a sub-message of what kind of init it is.  The CREATEINIT
  1002. sub-message  indicates that the object is initially being created.  This sub-message is in
  1003. case there is additional data initialization that must occur.  Depending on the object, there
  1004. may be handles that need to be created off the object itself.  It may not be a simple linear
  1005. block of data.  In these cases, you want to do this additional initialization when the
  1006. INITMESSAGE is received.
  1007. The WINDOWINIT sub-message is in case certain objects have a different state when the document
  1008. has a window than when it doesn't.  For example:  If there were a TextEdit object, then when
  1009. the file is initially read in and the CREATEINIT sub-message is passed to the object, there is
  1010. no window to pass to TENew to create a new TextEdit record.  The data has to be read in as
  1011. regular text that has no TERecord yet.  Once the document is assigned a window, a TERecord can
  1012. be created for the object and the text data can be moved into the TERecord.  This additional
  1013. WINDOWINIT sub-message is defined for just such objects that you may create.  Note that
  1014. nowhere in the application framework is there a call that passes a WINDOWINIT sub-message.
  1015. This would be an application-specific function, and therefore this call would belong in the
  1016. application.  The most likely place for this would be in the InitContent function for the
  1017. application.  You would do something like the following:
  1018.     DoFTreeMethod(root, INITMESSAGE, WINDOWINIT);
  1019. This would pass each object in the document an INITMESSAGE with a sub-message of WINDOWINIT.
  1020. The final sub-message is NOWINDOWINIT.  This message occurs when a document is being detached
  1021. from a window.  If an object has to change state to accomodate being related to a window, then
  1022. it would need to change state back when disassociated with that window.
  1023.  
  1024. FREEMESSAGE:  This is called due to DisposeChild being called.  DisposeChild will
  1025. dispose of simple objects that don't have additional handles of data.  Any handles that were
  1026. created at INITMESSAGE time should be disposed of when the FREEMESSAGE is received.
  1027.  
  1028. COPYMESSAGE:  When CopyChild is called, a new child is created via calling NewChild.
  1029. Normally when NewChild is called, the object is called with INITMESSAGE.  However,
  1030. when a child is being copied, the data area is copied into the copy child.  This data
  1031. copy would clobber any handle references the newly created copy would have, thus orphaning
  1032. those handles.  For this reason, no INITMESSAGE was passed to the object by NewChild.
  1033. The data was copied into the copy however, so any handle references in the copy aren't unique
  1034. references.  The original child has the same references.  This isn't a good situation.
  1035. The first thing that the code for the COPYMESSAGE needs to do is to send itself an
  1036. INITMESSAGE so that unique handles for the copy are created.  Once this is done, then
  1037. the code for COPYMESSAGE can actually copy the data in these handles.  Once this final
  1038. copying is done, we have a complete and separate copy of the original child.
  1039.  
  1040. UNDOMESSAGE:  When the user performs an undo or redo, involved objects get passed
  1041. this message.  When an undo/redo occurs objects are either moving into or out of the
  1042. document.  Objects move back and forth between the document and the undo hierarchy.
  1043. The sub-messages UNDOFROMDOC and UNDOTODOC determine the direction.  There may be various
  1044. tasks that need to be performed to this object and other parts of the document when an
  1045. undo/redo occurs.  When an undo of a DELETE_EDIT is performed, it is typical to select
  1046. the portion of the document undeleted.  The root object may hold the selection information.
  1047. If this is the case, the object should call GetRootHndl and then adjust the selection
  1048. accordingly.  Note that the root object may turn out to be the undo hierarchy root object.
  1049. If the object has been moved out of the document and into the undo hierarchy, this will
  1050. turn out to be the case.  In this instance nothing would have to be done.  At least one
  1051. message will be sent to the object while it is still in the document.  The sub-message
  1052. will then state whether the document is about to leave or just was added to the document.
  1053. Let's say that the root object contains a count of the number of selected items.  In this
  1054. case, if the item is selected, and it is about to leave the document, then the count of
  1055. items selected will need to be decremented.  If the item has just moved into the document,
  1056. then the item needs to be set as selected, plus the number of selected items count needs
  1057. to be increased by 1.
  1058. An additional call is made prior to any objects being moved.  This is to globally prepare
  1059. the document for an undo/redo operation.  Given that the items involved in the undo/redo
  1060. should be selected, this suggests that other selected items should be deselected prior
  1061. to the undo/redo.  This global undo/redo setup call is the place to do things like this.
  1062. Also, once the undo/redo operation is complete and all objects involved in the operation
  1063. are moved and messaged, a final call is made to clean up any unfinished undo/redo business.
  1064. To recap the undo/redo procedure:
  1065.     1) A global get-ready-to-undo/redo call is made.  This function is called UndoFixup.
  1066.        It is passed a reference to the document, plus a sub-message stating that it is
  1067.        called for pre-undo/redo tasks or post-undo/redo tasks (in this case pre-undo/redo).
  1068.     2) Each object involved in the undo/redo task is called with appropriate messages
  1069.        stating whether it is leaving or entering the document.  Appropriate document
  1070.        maintenance tasks should be performed based on these messages/sub-messages.
  1071.     1) UndoFixup is called a final time.  Once again, it is passed a reference to the document,
  1072.        plus a sub-message stating that it is called for post-undo/redo tasks.
  1073.  
  1074. CONVERTMESSAGE:  This is called to convert handle references within an object to treeID
  1075. values and visa versa, depending on the sub-message.
  1076. For the sub-message CONVERTTOID:  Prior to the first object being written to disk, DoNumberTree
  1077. is called to assign a unique treeID to each object in the document.  For each handle reference
  1078. in an object, call Hndl2ID to convert it to a treeID value.  Once the object is written to
  1079. disk, the object will be called again with the sub-message CONVERTTOHNDL.  This indicates that
  1080. the handle references that were converted to treeID values should be converted back.  Call
  1081. ID2Hndl to do the reverse conversion.
  1082. For the sub-message CONVERTTOHNDL:  The entire document is in memory prior to ever receiving this
  1083. message.  In the case of writing a document to disk, the document is already in memory.  For
  1084. the case where a document is being opened, the entire document is first read in, and then
  1085. objects are passed this message as an opportunity to convert treeID values into handle references.
  1086. In either case, DoNumberTree will have already been called, so it is okay to call ID2Hndl.
  1087.  
  1088. FREADMESSAGE:  This is called to read in the data portion of an object.  The header
  1089. information has already been read in.  Since the header information doesn't vary according
  1090. to the object type, it can be read in generically.  Also, the header information states what
  1091. type the object is, so until it the header is read in, the object type can't be determined.
  1092. If the data doesn't have any additional handle references, just call the default function
  1093. to read in the data.  The default function is called ReadTreeObjData.  It will read in
  1094. the number of bytes designated by the dataSize in the header, which has already been read in.
  1095. If there is additional data for the object to be kept in handles, or some such other unique
  1096. situation, the code to do this goes here.
  1097.  
  1098. FWRITEMESSAGE:  This is called to write out the data portion of an object.  The header
  1099. information has already been written.  Since the header information doesn't vary according
  1100. to the object type, it can be written generically.  If the data doesn't have any additional
  1101. handle references, just call the default function to write out the data.  The default
  1102. function is called WriteTreeObjData.  It will write out the number of bytes designated by
  1103. the dataSize in the header.  If there is additional data for the object kept in handles, or
  1104. some such other unique situation, the code to write this additional data goes here.
  1105.  
  1106. HREADMESSAGE:  This message is similar to FREADMESSAGE, except that the data is read from
  1107. a handle instead of from disk.  The assumption is that the data is already in a handle.
  1108. A handle containing the object's data is passed in.  The handle is the same size as the dataSize
  1109. field for the object.  If the object's data is flat, then you can simply do the following:
  1110.     case HREADMESSAGE:
  1111.         return(HReadTreeObjData(hndl, (Handle)data));
  1112.         break;
  1113. If the object's data isn't flat, then you will have to move the data from the handle into the
  1114. object as is appropriate for this object.  The ability to stream data not only to the file,
  1115. but to a handle is really convenient.  By calling the TreeObj.c function HWriteTree, you can
  1116. stream any tree (or branch) into a handle.  You can then save the handle as a resource, or you
  1117. can send it to another application via AppleEvents.  Once read, it can be unstreamed by
  1118. calling HReadTree.
  1119.  
  1120. HWRITEMESSAGE:  This message is similar to FWRITEMESSAGE, except that it is used to write
  1121. the data into a handle.  See the HREADMESSAGE for more info.
  1122.  
  1123. HITTESTMESSAGE:  This message is used for hit-testing of an object, along with various
  1124. targeting and keystroke information for the object.  See the DTS.Draw example for
  1125. implementation details.
  1126.  
  1127. GETRGNMESSAGE:  This message is requesting that the object return a region that describes
  1128. its shape.
  1129.  
  1130. GETBBOXMESSAGE:  This message is requesting that the object return a rectangle that encloses
  1131. all portions of the object.
  1132.  
  1133. SETBBOXMESSAGE:  This message is used to change the size of the bounding rectangle for
  1134. an object.
  1135.  
  1136. SECTBBOXMESSAGE:  This message is used to determine if the object's bounding rectangle
  1137. intersects the given rectangle.
  1138.  
  1139. TARGETMESSAGE:  This message is how an object is made the target or not the target of keystrokes.
  1140.  
  1141. DRAWMESSAGE:  This message and sub-message is to tell the object to draw itself, and in
  1142. what form.
  1143.  
  1144. PRINTMESSAGE:  This message is to tell the object to print itself.  Assumably the data
  1145. field will vary, according to the object type.  Objects often print themselves differently
  1146. than they draw, so specific object information will have to be passed in for these cases.
  1147.  
  1148. VHMESSAGE:  This message allows the object to format the data information that is viewed
  1149. when using the View Hierarchy object viewing feature.
  1150.  
  1151.  
  1152. As is evident from the above descriptions, the behaviors for the different types of objects
  1153. is completely dependent on what is done for the various messages.  To define an object type,
  1154. you need to make an entry into two tables.  These tables are:
  1155.     1) gTreeObjMethods
  1156.     2) gMinTreeObjSize
  1157. These tables are found in the application file File.c.  As you add objects to your application,
  1158. just make entries for these new objects into these tables.
  1159. If you want the default behaviors for an object, then use nil for the method procedure.  If
  1160. the method procedure is nil, then the object is passed no messages, as it is assumed that it
  1161. is simple and generic enough to be handled automatically.  If you need to handle just one
  1162. message specifically , you will then need to define a method procedure.  With method procedures,
  1163. it is an all-or-nothing situation.  If you set the method procedure to non-nil for an object,
  1164. then that object will receive all messages.  For these instances you can just call the default
  1165. code directly, such as ReadTreeObjData and WriteTreeObjData for handling file I/O for
  1166. the object.
  1167.  
  1168.  
  1169. Since the object is a handle of three components, referencing the object as if it is unique
  1170. can be a bit of a pain.  The unique portion of the object, the data portion, is actually in
  1171. the middle of the object.  To syntactically fix this problem, you will want to define some
  1172. macros for dereferencing into an object with appropriate object-type typecasting.  In
  1173. FileFormat.h you will find some such dereferencing macros.  They are in the form:
  1174.  
  1175. #define mDerefRoot(hndl)     ((RootObj*)((*hndl) + 1))
  1176.  
  1177. This macro allows you to write code that looks like the following:
  1178.  
  1179.     numSelected = mDerefRoot(root)->numSelected;
  1180.  
  1181. The only danger with this is that the handle that is dereferenced doesn't have to be of type
  1182. RootObj.  It can be of any type.  This is convenient and not.  It allows objects of different
  1183. types to be treated similarly or differently, whichever the code demands.
  1184.  
  1185.  
  1186. There are two defines in FileFormat.h that I should mention.  These are:
  1187. 1) MAXNUMUNDOS
  1188. 2) NUMSAVEUNDOS
  1189.  
  1190. These govern the depth of the undo mechanism.
  1191.  
  1192. MAXNUMUNDOS is the maximum number of undos that will be recorded before the oldest are
  1193. automatically purged.  This can be set up to 65535, if you so wish, although that will
  1194. cause a lot of undo hierarchy object to be kept around and is more than any human user
  1195. can comprehend.
  1196.  
  1197. NUMSAVEUNDOS is the maximum number of undos that are saved along with the document.  If this
  1198. number if non-zero, then when the document is opened, the user may already have some undos
  1199. that can be performed from the last editing session.  Setting this constant to zero makes
  1200. the application save no undos along with the document.
  1201.  
  1202.  
  1203. It is generally better if your object's data area is flat.  This is good for a number of reasons:
  1204.  
  1205. 1)    It allows you to use the default streaming functions for FREADMESSAGE, FWRITEMESSAGE,
  1206.     HREADMESSAGE, HWRITEMESSAGE.
  1207. 2)    Your application uses fewer handles.  The more handles, the slower the memory manager gets.
  1208.  
  1209. Of course, for some object, being completely flat is unreasonable.  The framework supports both flat
  1210. and non-flat objects, so the choice is yours.
  1211.  
  1212. */
  1213.  
  1214.  
  1215.  
  1216. void        DoNumberTree(TreeObjHndl hndl);
  1217.     /*
  1218.     **    ¶ Number each member in the tree with a unique treeID.
  1219.     **
  1220.     **    INPUT:    hndl        Any member of the hierarchy to be numbered.
  1221.     **
  1222.     **    Number each member in the tree with a unique treeID.  The tree is numbered sequentially from
  1223.     **    front to back.  The first treeID number is 1.  0 is reserved for Hndl2ID/ID2Hndl conversions
  1224.     **    where it is possible that the handle value is nil.  The nil handle will convert to 0, and
  1225.     **    convert back to nil. */
  1226.  
  1227.  
  1228.  
  1229. void        Hndl2ID(TreeObjHndl *hndl);
  1230.     /*
  1231.     **    ¶ Convert a handle reference into a treeID reference.
  1232.     **
  1233.     **    IN/OUT:    Pointer to handle to convert to an ID.  The ID is returned where the handle was.    
  1234.     **
  1235.     **    This function is used to convert a handle reference into a treeID reference.  A pointer to
  1236.     **    the handle reference is passed in.  Typical usage will be where a handle object has a
  1237.     **    reference to another handle object.  Handle object references aren't meaningful when saved
  1238.     **    to disk, and therefore don't persist in their native form.  These handle references need to
  1239.     **    be converted into a treeID reference so that they can be saved.  The tree first needs to be
  1240.     **    numbered so that the treeID references are unique and meaningful.  The tree is numbered by
  1241.     **    first calling DoNumberTree().  It numbers all the members of the tree hierarchy uniquely
  1242.     **    and sequentially. */
  1243.  
  1244.  
  1245.  
  1246. void        ID2Hndl(TreeObjHndl refHndl, TreeObjHndl *hndl);
  1247.     /*
  1248.     **    ¶ Return an object handle, given a tree object ID and reference object.
  1249.     **
  1250.     **    INPUT:    refHndl
  1251.     **    IN/OUT:    hndl        Input,  holds a tree object ID.
  1252.     **                        Output, holds an object handle.
  1253.     **    Given a tree object ID and a reference object (any member of the tree), return the
  1254.     **    cooresponding object handle.  DoNumberTree() must be called prior to using this function,
  1255.     **    and after the last change to the tree, as it generates the object treeID numbers for the
  1256.     **    entire tree. */
  1257.  
  1258.  
  1259.  
  1260. TreeObjHndl    GetUndoHndl(TreeObjHndl hndl);
  1261.     /*
  1262.     **    ¶ Given an object handle, return the undo handle.
  1263.     **
  1264.     **    INPUT:    hndl            Any handle in the hierarchy.
  1265.     **    RESULT:    TreeObjHndl        The root object for the undo hierarchy.
  1266.     **
  1267.     **    Given an object handle, return the undo handle. */
  1268.  
  1269.  
  1270.  
  1271. void        NewUndo(TreeObjHndl hndl);
  1272.     /*
  1273.     **    ¶ Close an old undo task, in preparation for a new one.
  1274.     **
  1275.     **    INPUT:    hndl    Any handle in the hierarchy (reference handle).
  1276.     **
  1277.     **    Used to close out an old undo task.  Closing out the old task means that any editing to the
  1278.     **    document will be recorded into a new undo task.  Use this to separate two edits of the same
  1279.     **    edit type that would otherwise get recorded into the same undo task. */
  1280.  
  1281.  
  1282.  
  1283. void        RevertEdit(TreeObjHndl hndl, Boolean fixup);
  1284.     /*
  1285.     **    ¶ If an edit fails, it can be backed out of by calling this.
  1286.     **
  1287.     **    INPUT:    hndl        Any handle in the hierarchy (reference handle).
  1288.     **            fixup        If true, then the document's undoFixup procedure is called
  1289.     **                        both before and after the undo/redo operation.
  1290.     **
  1291.     **    If an edit fails, it can be backed out of by calling this.  All edits that were done will be
  1292.     **    undone, thus recovering the state of the document prior to the edit. */
  1293.  
  1294.  
  1295.  
  1296. void        DoUndoTask(TreeObjHndl hndl, Boolean redo, Boolean fixup);
  1297.     /*
  1298.     **    ¶ Performs an undo/redo process for a document.
  1299.     **
  1300.     **    INPUT:    hndl        Any object in the document hierarchy.
  1301.     **            redo        True if this is a redo, as opposed to an undo operation.
  1302.     **            fixup        If true, then the document's undoFixup procedure is called
  1303.     **                        both before and after the undo/redo operation.
  1304.     **
  1305.     **    Call this to undo or redo editing to the document.  If redo is false, then it is an undo
  1306.     **    task.  If fixup is true, then the fixup application's undo fixup procedure is called before
  1307.     **    and after the undo operation. */
  1308.  
  1309.  
  1310.  
  1311. void        DisableUndo(TreeObjHndl hndl);
  1312.     /*
  1313.     **    ¶ Dispose of all undo information and prevent further undo collection.
  1314.     **
  1315.     **    INPUT:    hndl    Any object in the hierarchy.
  1316.     **
  1317.     **    Dispose of all undo information and prevent further undo collection.  Calling NewUndo
  1318.     **    will re-enable undo collection. */
  1319.  
  1320.  
  1321.  
  1322. void        DisposeUndos(TreeObjHndl hndl);
  1323.     /*
  1324.     **    ¶ Dispose of all undo information.
  1325.     **
  1326.     **    INPUT:    hndl    Any object in the hierarchy.
  1327.     **
  1328.     **    Dispose of all undo information. */
  1329.  
  1330.  
  1331.  
  1332. Boolean        PurgeUndo(TreeObjHndl hndl);
  1333.     /*
  1334.     **    ¶ Dispose of all undo information except the current undo.
  1335.     **
  1336.     **    INPUT:    hndl        Any object in the hierarchy.
  1337.     **    RESULT:    Boolean        True if something was purged.
  1338.     **
  1339.     **    Dispose of all undo information except the current undo.  The current undo may still be
  1340.     **    active, and it may be needed to back out of an edit operation. */
  1341.  
  1342.  
  1343.  
  1344. void        GetUndoInfo(FileRecHndl frHndl, short *undoDepth, short *numUndos);
  1345.     /*
  1346.     **    ¶ Return the depth of the undo state, along with the number of undos.
  1347.     **
  1348.     **    INPUT:    frHndl        File reference to get undo information for.
  1349.     **            undoDepth    Current depth into undo stack.
  1350.     **            numUndos    Depth of undo stack.
  1351.     **
  1352.     **    Return the depth of the undo state, along with the number of undos.  This is useful to
  1353.     **    determine if undo and redo operations are possible.  If undoDepth is non-zero, then undo
  1354.     **    is possible.  If numUndos is greater than undoDepth, then redo is possible. */
  1355.  
  1356.  
  1357.  
  1358. OSErr    DefaultFreeDocument(FileRecHndl frHndl);
  1359.     /*
  1360.     **    ¶ Dispose of the standard document and undo information.
  1361.     **
  1362.     **    INPUT:    frHndl        File reference getting freed.
  1363.     **    RESULT:    OSErr        Any error means that freeing may not have succeeded.
  1364.     **                        An error here is very unlikely, as most things that are
  1365.     **                        freed are handles, and they are simply disposed of.
  1366.     **
  1367.     **    This is called when the standard behavior of disposing of the standard document and undo
  1368.     **    hierarchies are to be disposed of.  Only call this if you have chosen to use the default
  1369.     **    document support of AppsToGo.
  1370.     **
  1371.     **    __________
  1372.     **
  1373.     **    Also see:    DefaultInitDocument. */
  1374.  
  1375.  
  1376.  
  1377. OSErr    DefaultInitDocument(FileRecHndl frHndl, short version, short numUndos, short numSaveUndos);
  1378.     /*
  1379.     **    ¶ Create the standard document and undo objects.
  1380.     **
  1381.     **    INPUT:    frHndl            File reference getting freed.
  1382.     **            version            Version information to save into document.
  1383.     **            numUndos        Number of undo levels to support (more undos, more memory).
  1384.     **            numSaveUndos    Number of undo levels to save with the document.
  1385.     **    RESULT:    OSErr            The most likely error is memFullErr.
  1386.     **
  1387.     **    This is called when the standard document behavior is what is desired.  Note that
  1388.     **    committing to the default document architecture means that you can call the other
  1389.     **    default document functions such as DefaultReadDocument, DefaultWriteDocument,
  1390.     **    DefaultFreeDocument, etc. */
  1391.  
  1392.  
  1393.  
  1394. OSErr    DefaultReadDocument(FileRecHndl frHndl);
  1395.     /*
  1396.     **    ¶ Dispose of the standard document and undo information.
  1397.     **
  1398.     **    INPUT:    frHndl        File reference getting freed.
  1399.     **    RESULT:    OSErr        Any error means that freeing may not have succeeded.
  1400.     **                        An error here is very unlikely, as most things that are
  1401.     **                        freed are handles, and they are simply disposed of.
  1402.     **
  1403.     **    This is called when the standard behavior of disposing of the standard document and undo
  1404.     **    hierarchies are to be disposed of.  Only call this if you have chosen to use the default
  1405.     **    document support of AppsToGo.
  1406.     **
  1407.     **    __________
  1408.     **
  1409.     **    Also see:    DefaultInitDocument. */
  1410.  
  1411.  
  1412.  
  1413. OSErr    DefaultReadDocumentFixup(FileRecHndl frHndl);
  1414.     /*
  1415.     **    ¶ Move undo tasks saved with document out of document and into undo hierarchy.
  1416.     **
  1417.     **    INPUT:    frHndl        File reference to do fixup to.
  1418.     **    RESULT:    OSErr        Most likely is memFullErr.
  1419.     **
  1420.     **    Move any undo tasks that were saved with the document out of the document and into the
  1421.     **    undo hierarchy.  They were moved into the document when the document was saved so that
  1422.     **    the undo information could be saved with the document. */
  1423.  
  1424.  
  1425.  
  1426. OSErr    DefaultWriteDocument(FileRecHndl frHndl);
  1427.     /*
  1428.     **    ¶ Write a standard hierarchical document to disk.
  1429.     **
  1430.     **    INPUT:    frHndl        File reference for document to save.
  1431.     **    RESULT:    OSErr
  1432.     **
  1433.     **    Write a standard hierarchical document to disk.  The number of undos to save (indicated by
  1434.     **    the numSaveUndos field in the undo root) are first moved to the end of the document.  The
  1435.     **    document is then saved starting with the document root. */
  1436.  
  1437.  
  1438.  
  1439.     /* These functions are called when the standard behaviors are what is desired.
  1440.     **
  1441.     ** DefaultInitDocument:
  1442.     **        Create a root object and an undo root object.  The undo root has the numUndos
  1443.     **        and numSaveUndos values placed in it.
  1444.     **
  1445.     ** DefaultFreeDocument:
  1446.     **        Dispose of the document and undo information.  It assumes a normal hierarchical
  1447.     **        document architecture and undo root for the document. 
  1448.     **
  1449.     ** DefaultReadDocument:
  1450.     **        Read a standard hierarchical document from a file.  It assumes that the document
  1451.     **        was written with DefaultWriteDocument.  There may be handle-to-id conversions
  1452.     **        still to do.  The entire document must first be read in before this can occur.
  1453.     **        A separate pass through the doucment is made, giving each object a chance to
  1454.     **        convert id's back into handle references.
  1455.     **
  1456.     ** DefaultReadDocumentFixup:
  1457.     **        Fixup the document that was just read in.  When a document is written out using
  1458.     **        DefaultWriteDocument, some number of undo tasks may have been moved onto the
  1459.     **        end of the document.  This function moves the undo tasks out of the document
  1460.     **        and into the undo root.
  1461.     **
  1462.     ** DefaultWriteDocument:
  1463.     **        Write a standard hierarchical document to disk.  The number of undos to save
  1464.     **        (indicated by the numSaveUndos field in the undo root) are first moved to the
  1465.     **        end of the document.  The document is then saved starting with the document
  1466.     **        root.
  1467.     */
  1468.  
  1469.  
  1470.  
  1471. OSErr    HReadTree(TreeObjHndl hndl, Handle tree);
  1472.     /*
  1473.     **    ¶ Unflatten a hierarchy that is flattened in the handle "tree".
  1474.     **
  1475.     **    INPUT:    hndl        Handle which will serve as the root for the hierarchy when unflattened.
  1476.     **                        This doesn't have to be a true root.  It can be an existing node in a
  1477.     **                        hierarchy.  A branch will be created in this case.
  1478.     **            tree        The handle containing the flattened hierarchy.
  1479.     **    RESULT:    OSErr
  1480.     **
  1481.     **    HReadTree stands for Handle-based ReadTree.
  1482.     **
  1483.     **    Given a handle containing a flatten hierarchy created via WriteTree, this function is called
  1484.     **    to unflatten the data into a hierarchy. */
  1485.  
  1486.  
  1487.  
  1488. OSErr    HReadBranch(TreeObjHndl hndl, Handle tree);
  1489.     /*
  1490.     **    ¶ Recursively dissects the handle into separate tree objects.
  1491.     **
  1492.     **    INPUT:    hndl        Root object (root or branch) to unflatten data into.
  1493.     **            tree        Handle holding a flattened hierarchy.
  1494.     **    RESULT:    OSErr
  1495.     **
  1496.     *    HReadTree calls this to get the real work done.  HReadTree is a front-end to this function
  1497.     **    for applications.
  1498.     **
  1499.     **    This function recursively dissects the handle into separate tree objects.  The handle has
  1500.     **    previously had tree objects streamed into it.  The public format is as follows:
  1501.     **        1) object header
  1502.     **        2) data length (4 bytes)
  1503.     **        3) data
  1504.     **
  1505.     **    After the object is created, the header is moved into it.  Then the data length is fetched
  1506.     **    and a data handle is created to hold the data portion of the streamed data for this object.
  1507.     **    Once the data handle holds the object data, the tree handle has the information for this
  1508.     **    object removed from it.
  1509.     **    After this data separation, we call the object and pass it the data handle. The object is
  1510.     **    responsible for interpreting the handle and initializing the data portion of the object
  1511.     **    with it. */
  1512.  
  1513.  
  1514.  
  1515. OSErr    HReadTreeObjData(TreeObjHndl hndl, Handle treeObjData);
  1516.     /*
  1517.     **    ¶ Get the object data from the stream.
  1518.     **
  1519.     **    INPUT:    hndl            Handle to receive object data.
  1520.     **            treeObjData        Remains of flattened hierarchy.
  1521.     **    RESULT:    OSErr
  1522.     **
  1523.     **    The simple handle read can assume that there will be no expansion or interpretation of the
  1524.     **    data.  This means that the dataSize field represents the correct data size. */
  1525.  
  1526.  
  1527.  
  1528. OSErr    HWriteTree(TreeObjHndl hndl, Handle tree);
  1529.     /*
  1530.     **    ¶ Stream the hierarchy onto the end of the handle.
  1531.     **
  1532.     **    INPUT:    hndl    Root object of document or branch to stream into handle.
  1533.     **            tree    Handle receive flattened data.  Data is appended to end of handle.
  1534.     **
  1535.     **    Given a handle, this function is called to stream the hierarchy onto the end
  1536.     **    of the handle. */
  1537.  
  1538.  
  1539.  
  1540. OSErr    HWriteTreeObjData(TreeObjHndl hndl, Handle data);
  1541.     /*
  1542.     **    ¶ Append data in object onto end of handle receiving streamed hierarchy.
  1543.     **
  1544.     **    INPUT:    hndl    Object whose data is to be appended to the receiving handle.
  1545.     **            data    Handle receiving flattened hierarchy, and in this case the data
  1546.     **                    data portion of a single object.  The object data is appended to
  1547.     **                    the end of the handle.
  1548.     **    RESULT:    OSErr
  1549.     **
  1550.     **    Given an object, append its data onto the end of a handle which is receiving a flattened
  1551.     **    hierarchy.  Each object is streamed into the flattened hierarchy handle in two steps:
  1552.     **        1)    The header of the object.
  1553.     **        2)    The data area of the object.
  1554.     **
  1555.     **    HWriteTreeObjData is the default action for step 2. */
  1556.  
  1557.  
  1558.  
  1559. long            GetCData(TreeObjHndl hndl, long offset, char *data);
  1560.     /*
  1561.     **    ¶ Given an offset in the object's data, get the 0-terminated data.
  1562.     **
  1563.     **    INPUT:    hndl        Object to get data from.
  1564.     **            offset        Offset into the data area of the object.
  1565.     **            data        Pointer to where to place the received data.
  1566.     **                        (If nil, no data transfer occurs, but the length is still returned.)
  1567.     **    RESULT:    long        Length of data gotten (or measured).
  1568.     **
  1569.     **    This is used to copy 0-terminated data out of an object, given the offset of the data.
  1570.     **    The offset is generally gotten by calling GetDataOffset for complex data types.  If the
  1571.     **    data for the object is a single c-string (or a c-string at the end of a fixed block), then
  1572.     **    you can simply use the macro “offsetof” to get the offset to the c-string.  If you have
  1573.     **    multiple packed c-strings, then you need to use GetDataOffset to determine the offset for
  1574.     **    c-strings after the first.  (The first starts at a fixed location, so therefore it has
  1575.     **    a constant offset.)
  1576.     **
  1577.     **    __________
  1578.     **
  1579.     **    Also see:    PutCData, GetDataOffset. */
  1580.  
  1581.  
  1582.  
  1583. OSErr            PutCData(TreeObjHndl hndl, long offset, char *data);
  1584.     /*
  1585.     **    ¶ Given an offset in the object's data, replace the old 0-terminated data.
  1586.     **
  1587.     **    INPUT:    hndl        Object to store data.
  1588.     **            offset        Offset into the data area of the object.
  1589.     **            data        Pointer to the data to save.
  1590.     **    RESULT:    OSErr        Most likely error is memFullErr.
  1591.     **
  1592.     **    This is used to save 0-terminated data into an object, given the offset of the data.
  1593.     **    The offset must reference a 0-terminated data location, since this operation will
  1594.     **    replace the previous 0-terminated data element.
  1595.     **
  1596.     **    The offset is generally gotten by calling GetDataOffset for complex data types.  If the
  1597.     **    data for the object is a single c-string (or a c-string at the end of a fixed block), then
  1598.     **    you can simply use the macro “offsetof” to get the offset to the c-string.  If you have
  1599.     **    multiple packed c-strings, then you need to use GetDataOffset to determine the offset for
  1600.     **    c-strings after the first.  (The first starts at a fixed location, so therefore it has
  1601.     **    a constant offset.)
  1602.     **
  1603.     **    __________
  1604.     **
  1605.     **    Also see:    GetCData, GetDataOffset. */
  1606.  
  1607.  
  1608.  
  1609. void            GetPData(TreeObjHndl hndl, long offset, StringPtr data);
  1610.     /*
  1611.     **    ¶ Given an offset in the object's data, get the pascal string data.
  1612.     **
  1613.     **    INPUT:    hndl        Object to get data from.
  1614.     **            offset        Offset into the data area of the object.
  1615.     **            data        Pointer to where to place the received data.
  1616.     **                        (If nil, no data transfer occurs, but the length is still returned.)
  1617.     **    RESULT:    long        Length of data gotten (or measured).
  1618.     **
  1619.     **    This is used to copy pascal string data out of an object, given the offset of the data.
  1620.     **    The offset is generally gotten by calling GetDataOffset for complex data types.  If the
  1621.     **    data for the object is a single pascal-string (or a pascal-string at the end of a fixed
  1622.     **    block), then you can simply use the macro “offsetof” to get the offset to the pascal-string.
  1623.     **    If you have multiple packed pascal-strings, then you need to use GetDataOffset to determine
  1624.     **    the offset for pascal-strings after the first.  (The first starts at a fixed location, so
  1625.     **    therefore it has a constant offset.)
  1626.     **
  1627.     **    __________
  1628.     **
  1629.     **    Also see:    PutPData, GetDataOffset. */
  1630.  
  1631.  
  1632.  
  1633. OSErr            PutPData(TreeObjHndl hndl, long offset, StringPtr data);
  1634.     /*
  1635.     **    ¶ Given an offset in the object's data, replace the old pascal data.
  1636.     **
  1637.     **    INPUT:    hndl        Object to store data.
  1638.     **            offset        Offset into the data area of the object.
  1639.     **            data        Pointer to the data to save.
  1640.     **    RESULT:    OSErr        Most likely error is memFullErr.
  1641.     **
  1642.     **    This is used to save a pascal string into an object, given the offset of the data.
  1643.     **    The offset must reference a pascal string location, since this operation will
  1644.     **    replace the previous pascal string data element.
  1645.     **
  1646.     **    The offset is generally gotten by calling GetDataOffset for complex data types.  If the data
  1647.     **    for the object is a single pascal-string (or a pascal-string at the end of a fixed block),
  1648.     **    then you can simply use the macro “offsetof” to get the offset to the pascal-string.  If you
  1649.     **    have multiple packed pascal-strings, then you need to use GetDataOffset to determine the
  1650.     **    offset for pascal-strings after the first.  (The first starts at a fixed location, so
  1651.     **    therefore it has a constant offset.)
  1652.     **
  1653.     **    __________
  1654.     **
  1655.     **    Also see:    GetPData, GetDataOffset. */
  1656.  
  1657.  
  1658.  
  1659. OSErr            PutShortData(TreeObjHndl hndl, long offset, void *data, unsigned short size);
  1660.     /*
  1661.     **    ¶ Store block of short data (data length < 64k) into object, replacing old block.
  1662.     **
  1663.     **    INPUT:    hndl        Object to store data into.
  1664.     **            offset        Offset (from beginning of data area) of where to store data.
  1665.     **                        Note that this is a replace action, so there needs to be a
  1666.     **                        short data element already at offset.    
  1667.     **            data        Pointer to short data.
  1668.     **            size        Size of data.
  1669.     **    RESULT:    OSErr
  1670.     **
  1671.     **    This is used to store blocks of data of max size < 64k into the data area of the object.
  1672.     **    Note that this operation replaces the previous short data block, so therefore the offset
  1673.     **    must be validly referencing a short data element in the object.
  1674.     **
  1675.     **    Below is some sample code which demonstrates saving a TextEdit text data block and
  1676.     **    style block into an AppsToGo CtlObj object:
  1677.     **
  1678.     **        hh = (*te)->hText;
  1679.     **        nn = (*te)->teLength;
  1680.     **        oo = GetDataOffset(cobj, offsetof(CtlObj,title), kPStr, 4);
  1681.     **        HLock(hh);
  1682.     **        PutShortData(cobj, oo, *hh, nn);
  1683.     **        HUnlock(hh);
  1684.     **
  1685.     **        oo = GetDataOffset(cobj, oo, kSDataBlock, 1);
  1686.     **        if (styl = CTEGetFullStylScrap(te)) {
  1687.     **            HLock((Handle)styl);
  1688.     **            PutShortData(cobj, oo, (Ptr)*styl, GetHandleSize((Handle)styl));
  1689.     **            HUnlock((Handle)styl);
  1690.     **        }
  1691.     **        else
  1692.     **            PutShortData(cobj, oo, nil, 0);
  1693.     **
  1694.     **    The first GetDataOffset starts at the fixed offset where the pascal string data starts.
  1695.     **    It skips over the 4 pascal strings to return the offset to the first short data block,
  1696.     **    which is the text of the TextEdit record.  Once the offset is obtained, the text handle
  1697.     **    is locked, and then PutShortData is called to replace the old text block in the object.
  1698.     **
  1699.     **    Once the text block is replaced, GetDataOffset is used to get the offset of the next
  1700.     **    short block of data, which is stored immediately after the text block.  The data is then
  1701.     **    saved via a second call to PutShortData.
  1702.     **
  1703.     **    Note that when writing the style block, we don't even necessarily have a style block.
  1704.     **    Ths styl handle may be returned as nil.  In this case, we write out 0 bytes, thus replacing
  1705.     **    the old data block with an empty data block. */
  1706.  
  1707.  
  1708.  
  1709. OSErr            PutLongData(TreeObjHndl hndl, long offset, void *data, long size);
  1710.     /*
  1711.     **    ¶ Store block of long data (length which may be > 64k) into object, replacing old block.
  1712.     **
  1713.     **    INPUT:    hndl        Object to store data into.
  1714.     **            offset        Offset (from beginning of data area) of where to store data.
  1715.     **                        Note that this is a replace action, so there needs to be a
  1716.     **                        long data element already at offset.    
  1717.     **            data        Pointer to long data.
  1718.     **            size        Size of data.
  1719.     **    RESULT:    OSErr
  1720.     **
  1721.     **    This is used to store blocks of data into the data area of the object.  Note that this
  1722.     **    operation replaces the previous short data block, so therefore the offset must be validly
  1723.     **    referencing a short data element in the object.
  1724.     **
  1725.     **    Below is some sample code which demonstrates writing a handle of data "hh" into the
  1726.     **    object at offset "oo":
  1727.     **
  1728.     **        HLock(hh);
  1729.     **        PutLongData(cobj, oo, *hh, GetHandleSize(hh));
  1730.     **        HUnlock(hh);
  1731.     **
  1732.     **    For an example of determining the offset to place the data, see PutShortData.  The only
  1733.     **    difference between the two calls is the size of the data element.  How you use the calls
  1734.     **    is the same. */
  1735.  
  1736.  
  1737.  
  1738. unsigned long    GetDataOffset(TreeObjHndl hndl, unsigned long offset, short dtype, short dnum);
  1739.     /*
  1740.     **    ¶ Get pointer into data area of object for complex data area definitions.
  1741.     **
  1742.     **    INPUT:    hndl        Handle to reference into to generate offset.
  1743.     **            offset        Starting offset to advance from.
  1744.     **            dtype        Type of data to traverse.
  1745.     **            dnum        Number of data elements to traverse.
  1746.     **    RESULT:    long        Advanced offset value after traversing data elements.
  1747.     **
  1748.     **    GetDataOffset is the key call.  It allows you to calculate an offset into the objects data,
  1749.     **    even if the data in front of it is of variable size.  Let's takle a look at an object that is
  1750.     **    quite variable.  You will find the following typedef in the file DTS.Lib.h:
  1751.     **
  1752.     **    typedef struct {
  1753.     **        short    numRows;
  1754.     **        short    numCols;
  1755.     **        short    cellHeight;
  1756.     **        short    cellWidth;
  1757.     **        short    mode;
  1758.     **    } CLNewInfo;
  1759.     **    typedef struct {
  1760.     **        Rect    destRct;
  1761.     **        Rect    viewRct;
  1762.     **        Rect    brdrRct;
  1763.     **        short    maxTextLen;
  1764.     **        short    mode;
  1765.     **    } CTENewInfo;
  1766.     **    typedef struct {
  1767.     **        short            selected;
  1768.     **        Rect            rect;
  1769.     **        char            visible;
  1770.     **        char            hilite;
  1771.     **        short            value, min, max;
  1772.     **        short            procID;
  1773.     **        long            refCon;
  1774.     **        short            ctlID;
  1775.     **        short            cctbID;
  1776.     **        short            fontSize;
  1777.     **        Style            fontStyle;
  1778.     **        union {
  1779.     **            CLNewInfo    clnew;
  1780.     **            CTENewInfo    ctenew;
  1781.     **        } extCtl;
  1782.     **        unsigned char    title[4];        4 pascal strings are stored back-to-back starting here.
  1783.     **                                        title[0] = control title
  1784.     **                                        title[1] = key equivs
  1785.     **                                        title[2] = control font
  1786.     **                                        title[3] = balloon help info
  1787.     **        short            textLen[2];        2 short-prefixed data blocks stored starting here.
  1788.     **                                        textLen[0] = textEdit control text block
  1789.     **                                        textLen[1] = textEdit control style block
  1790.     **    } CtlObj;
  1791.     **    #define mDerefCtl(hndl) ((CtlObj*)((*hndl) + 1))
  1792.     **
  1793.     **    Everything is reasonable until we get to the textLen field.  If we were to get the offset
  1794.     **    of this field using the offsetof macro (found in Utilities.h), we would get a value that is
  1795.     **    4 bigger than the offset for the title field.  This is initially true if the struct is filled
  1796.     **    with zeros.  (We would then have 4 zero-length pascal strings in front on textLen).  Once we
  1797.     **    modify one of these title strings, then the textLen data would slide down.
  1798.     **
  1799.     **    There are 4 data types that GetDataOffset can traverse.  They are:
  1800.     **        #define kCStr       0        (See GetCData and PutCData)
  1801.     **        #define kPStr       1        (See GetPData and PutPData)
  1802.     **        #define kSDataBlock 2        (See PutShortData)
  1803.     **        #define kLDataBlock 3        (See PutLongData)
  1804.     **
  1805.     **    To get the correct offset for textLen[1], we would do the following:
  1806.     **
  1807.     **    ofst = GetDataOffset(hndl, offsetof(CtlObj,title), kPStr, 4);
  1808.     **                This gets us past the 4 pascal strings.
  1809.     **
  1810.     **    ofst = GetDataOffset(hndl, ofst, kSDataBlock, 1);
  1811.     **            This gets us past the first short block of data.  A short block of data is a 2-byte
  1812.     **            data length, followed by the data.  Note that even though textLen is a signed short,
  1813.     **            GetDataOffset treats it as unsigned.  This allows us to be a bit more descriptive
  1814.     **            in our typedef.  By stating that it is a short, we are indicating that we don't
  1815.     **            expect the data to get bigger than 32767.
  1816.     **
  1817.     **    Now that we have this offset, we can use the PutShortData() call to replace this data.
  1818.     **    Let's assume that we have a handle of text called txtHndl, and the length is txtLen.  To
  1819.     **    replace the old block of data with this text, we would make the following call:
  1820.     **
  1821.     **        err = PutShortData(hndl, ofst, txtHndl, txtLen);
  1822.     **
  1823.     **    Any data fields that are after this field are moved to adjust for the size differences
  1824.     **    between the data being replaced and the new data.
  1825.     **
  1826.     **    Long data blocks are also supported, as well as C and pascal string data types.
  1827.     **    There are also Get functions for the string types, but there aren't for the block types.
  1828.     **    The assumption is that you will need to create a handle to copy the data into.  To get a
  1829.     **    data block, first generate the offset, then get the short or long for the data size.  Create
  1830.     **    a handle of this size, and then BlockMove the data.
  1831.     **
  1832.     **    To get the data for this short block, the code would look like this:
  1833.     **
  1834.     **        short    ofst;
  1835.     **        Ptr        dptr;
  1836.     **
  1837.     **        ofst = GetDataOffset(hndl, offsetof(CtlObj,title), kPStr, 4);
  1838.     **        ofst = GetDataOffset(hndl, ofst, kSDataBlock, 1);
  1839.     **        dptr = GetDataPtr(hndl);
  1840.     **        BlockMove(dptr + ofst, &dsiz, sizeof(short));
  1841.     **        dhndl = NewHandle(dsiz);
  1842.     **        if (dhndl) {
  1843.     **            dptr = GetDataPtr(hndl);
  1844.     **            BlockMove(dptr + ofst, *dhndl, dsiz);
  1845.     **        }
  1846.     **
  1847.     **    Not too bad for fetching a variable-length field from a variable location.
  1848.     **
  1849.     **    Of course, for strings, it's a snap.  Here's the code to get title[2]:
  1850.     **
  1851.     **        short    ofst;
  1852.     **        Str255    pstr;
  1853.     **
  1854.     **        ofst = GetDataOffset(hndl, offsetof(CtlObj,title), kPStr, 2);
  1855.     **        GetPData(hndl, ofst, pstr);
  1856.     */
  1857.  
  1858.  
  1859. Boolean    EqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2);
  1860.     /*
  1861.     **    ¶ Compare the data area of two TreeObj objects.
  1862.     **
  1863.     **    INPUT:    h1            One of two handles for compare.
  1864.     **            h2            Other handle for compare.
  1865.     **    RESULT:    Boolean        True if data areas are the same.
  1866.     **
  1867.     **    Call this function to compare two TreeObj objects.  If there is no procPtr defined for the
  1868.     **    object, then this function calls DefaultEqualTreeObjData to do the comparison.  If you have
  1869.     **    a procPtr defined for the function, then you will probably want to call
  1870.     **    DefaultEqualTreeObjData from within your object when you get a COMPAREMESSAGE.
  1871.     **    DefaultEqualTreeObjData simply does a byte-per-byte comparison, and if any difference is
  1872.     **    found, it returns false.  If all of the bytes are the same, then it returns true.  For deep
  1873.     **    objects, you will have to write your own comparison.  If you have a handle stored in the
  1874.     **    object, then you will need to compare the contents of the handle, instead of the handle
  1875.     **    itself.  DefaultEqualTreeObjData doesn't know that the data is actually a handle.
  1876.     **    DefaultEqualTreeObjData does a flat compare. */
  1877.  
  1878. Boolean    DefaultEqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2);
  1879.     /*
  1880.     **    ¶ The default object comparison function.
  1881.     **
  1882.     **    INPUT:    h1            One of two handles for compare.
  1883.     **            h2            Other handle for compare.
  1884.     **    RESULT:    Boolean        True if data areas are the same.
  1885.     **
  1886.     **    The default object comparison function.  This simply does a flat-compare of the
  1887.     **    two objects.
  1888.     **
  1889.     **    __________
  1890.     **
  1891.     **    Also see:    EqualTreeObjData. */
  1892.  
  1893.  
  1894. #endif
  1895.